classdef LabelPowerset ...
        < MultiLabelAlgorithm ...
        & BinaryClassAlgorithm0able ...
        & ZAlgorithmable ...
        & ZSubLabelsetable ...
        & DecisionThresholdable ...
        & MultiLabelMetricable
    %LABELPOWERSET Summary of this class goes here
    %   Detailed explanation goes here
    
    properties
    end
    
    methods
        function [ this ] = LabelPowerset(  )
            if nargin == 0
                this.setName('lp');
            end
        end
    end
    
    methods
        function [  ] = updateZSubLabelset( this, Y )
            %             Y(Y == -1) = 0; % For using @find later
            zLabelPowerset = unique(Y, 'rows');
            this.zSubLabelset = num2cell(zLabelPowerset, 2)';
            
            %             this.zSubLabelset = cellfun(@find, this.zSubLabelset, 'UniformOutput', false);
        end
    end
    
    methods
        function [  ] = build( this, X1, Y1 )
            %BUILD Summary of this function goes here
            %   Detailed explanation goes here
            % LabelPowerset::build
            
            % nLab = size(Y1, 2);
            % this.zLabelset = generateZLabelset(this.randomSeed, nLab, this.nSubLabel);
            this.updateZSubLabelset(Y1);
            
            nLabelset = length(this.zSubLabelset);
            this.zAlgorithm = cell(1, nLabelset);
            
            for iLabelset = 1:nLabelset
                Y1_i = bsxfun(@eq, Y1, this.zSubLabelset{iLabelset});
                Y1_i = all(Y1_i, 2);
                Y1_i = double(Y1_i);
                Y1_i(Y1_i ~= 1) = -1;
                
                this.zAlgorithm{iLabelset} = this.algorithm0.clone();
                this.zAlgorithm{iLabelset}.build(X1, Y1_i);
            end
        end
        
        function [ result, Y2_hat, Y2_out ] = apply( this, X2, Y2 )
            %APPLY Summary of this function goes here
            %   Detailed explanation goes here
            % LabelPowerset::apply
            % keyboard
            nLabelset = length(this.zSubLabelset);
            
            Y2_hat = zeros(size(Y2));
            Y2_out = Y2_hat;
            
            for iLabelset = 1:nLabelset
                Y2_i = bsxfun(@eq, Y2, this.zSubLabelset{iLabelset});
                Y2_i = all(Y2_i, 2);
                Y2_i = double(Y2_i);
                Y2_i(Y2_i ~= 1) = -1;
                
                temp_result = this.zAlgorithm{iLabelset}.apply(X2, Y2_i);
                Y2_out_i = temp_result.Y_out;
                Y2_out(:, this.zSubLabelset{iLabelset} == 1) ...
                    = Y2_out(:, this.zSubLabelset{iLabelset} == 1) ...
                    + repmat(Y2_out_i, 1, nnz(this.zSubLabelset{iLabelset} == 1));
            end
            
            Y2_hat(Y2_out > this.decisionThreshold) = 1;
            Y2_hat(Y2_hat ~= 1) = -1;
            Y2_out = Y2_out/nLabelset;
            result.Y_hat = Y2_hat;
            result.Y_out = Y2_out;
        end
    end
    
end

